use crate::{addrspace::PhysAddr, memblock::common::memblock_insert_region};

use super::{
    common::{memblock_cap_size, memblock_merge_regions, memblock_remove_range},
    Memblock, MemblockFlags, MemblockType,
};

fn memblock_add_range(
    ty: &mut MemblockType,
    mut base: PhysAddr,
    mut size: usize,
    flags: MemblockFlags,
) -> Result<(), ()> {
    let mut insert = false;
    let obase = base;
    let end = base + memblock_cap_size(base, &mut size);

    if size == 0 {
        return Err(());
    }

    if ty.regions[0].size == 0 {
        debug_assert!(ty.cnt == 1 && ty.total_size == 0);
        ty.regions[0].base = base;
        ty.regions[0].size = size;
        ty.regions[0].flags = flags;
        ty.total_size = size;
        return Ok(());
    }

    loop {
        let mut nr_new = 0;
        let mut idx = 0;
        base = obase;

        while idx < ty.cnt {
            let rgn = ty.regions[idx];
            let rbase = rgn.base;
            let rend = rbase + rgn.size;

            if rbase >= end {
                break;
            }
            if rend <= base {
                idx += 1;
                continue;
            }
            if rbase > base {
                debug_assert_eq!(flags, rgn.flags);
                nr_new += 1;
                if insert {
                    memblock_insert_region(ty, idx, base, (rbase - base).value(), flags);
                    idx += 1;
                }
            }
            base = rend.min(end);
            idx += 1;
        }

        if base < end {
            nr_new += 1;
            if insert {
                memblock_insert_region(ty, idx, base, (end - base).value(), flags);
            }
        }

        if nr_new == 0 {
            return Ok(());
        }
        if !insert {
            if ty.cnt + nr_new > ty.max {
                return Err(());
            }
            insert = true;
        } else {
            memblock_merge_regions(ty);
            return Ok(());
        }
    }
}

impl Memblock {
    /// 向 MemBlock 添加可用内存
    ///
    /// 注意: 如果操作后预留内存域不是可用内存域的子集, 那么之后的 alloc, iter, mark 行为将未定义
    pub fn add(&mut self, base: PhysAddr, size: usize) -> Result<(), ()> {
        memblock_add_range(&mut self.memory, base, size, MemblockFlags::MEMBLOCK_NONE)
    }

    /// 向 MemBlock 添加预留内存
    ///
    /// 当内存域被添加到预留内存后, 之后使用 alloc 接口将会避开预留内存区域进行分配
    ///
    /// 注意: 如果操作后预留内存域不是可用内存域的子集, 那么之后的 alloc, iter, mark 行为将未定义
    pub fn reserve(&mut self, base: PhysAddr, size: usize) -> Result<(), ()> {
        memblock_add_range(&mut self.reserved, base, size, MemblockFlags::MEMBLOCK_NONE)
    }

    /// 从 MemBlock 的可用内存区域移除内存域
    ///
    /// 注意: 如果操作后预留内存域不是可用内存域的子集, 那么之后的 alloc, iter, mark 行为将未定义
    pub fn remove(&mut self, base: PhysAddr, size: usize) -> Result<(), ()> {
        memblock_remove_range(&mut self.memory, base, size)
    }
}
